from src.compiler.Lexer.tokens import Token, TokenType
from src.compiler.Parser.ast import *
from src.compiler.Parser.grammar import NonTerminal, Terminal, PRODUCTIONS
from src.compiler.Parser.ll1_analyzer import LL1Analyzer
from collections import deque


class ParserError(Exception):
    """语法分析错误异常类"""

    def __init__(self, message, line, column, expected=None):
        if expected:
            message = f"{message}. Expected: {expected}"
        super().__init__(f"{message} at line {line}, column {column}")
        self.line = line
        self.column = column
        self.expected = expected


class ActionSymbol:
    """一个特殊的符号，用于触发AST构建动作"""

    def __init__(self, non_terminal, production):
        self.non_terminal = non_terminal
        self.production = production

    def __repr__(self):
        prod_str = ' '.join(str(s) for s in self.production)
        return f"#ACTION({self.non_terminal.name} -> {prod_str})"




class PredictiveParser:
    """预测分析语法分析器"""

    def __init__(self, tokens):
        self.tokens = tokens
        self.current_token_index = 0
        self.current_token = tokens[0] if tokens else None

        # 初始化LL(1)分析器
        self.analyzer = LL1Analyzer()
        self.analyzer.compute_first_sets()
        self.analyzer.compute_follow_sets()
        self.predictive_table = self.analyzer.build_predictive_table()

        # 分析栈和输出记录
        self.stack = deque()
        self.output = []  # 存储四元式输出
        self.step = 0

        # AST构建状态
        self.ast_build_stack = deque()

    def _token_to_terminal(self, token):
        """将Token转换为终结符"""
        if not token or token.type == TokenType.EOF:
            return Terminal.EOF

        token_type_to_terminal = {
            TokenType.KEYWORD_SELECT: Terminal.SELECT,
            TokenType.KEYWORD_FROM: Terminal.FROM,
            TokenType.KEYWORD_WHERE: Terminal.WHERE,
            TokenType.KEYWORD_CREATE: Terminal.CREATE,
            TokenType.KEYWORD_TABLE: Terminal.TABLE,
            TokenType.KEYWORD_INSERT: Terminal.INSERT,
            TokenType.KEYWORD_INTO: Terminal.INTO,
            TokenType.KEYWORD_VALUES: Terminal.VALUES,
            TokenType.KEYWORD_DELETE: Terminal.DELETE,
            TokenType.KEYWORD_UPDATE: Terminal.UPDATE,
            TokenType.KEYWORD_SET: Terminal.SET,
            TokenType.KEYWORD_INTEGER: Terminal.INTEGER,
            TokenType.KEYWORD_VARCHAR: Terminal.VARCHAR,
            TokenType.KEYWORD_FLOAT: Terminal.FLOAT,
            TokenType.KEYWORD_BOOLEAN: Terminal.BOOLEAN,
            TokenType.KEYWORD_DATE: Terminal.Date,
            TokenType.KEYWORD_IS: Terminal.IS,
            TokenType.KEYWORD_IN: Terminal.IN,
            TokenType.OPERATOR_MUL: Terminal.ASTERISK,
            TokenType.DELIMITER_COMMA: Terminal.COMMA,
            TokenType.DELIMITER_SEMICOLON: Terminal.SEMICOLON,
            TokenType.DELIMITER_LPAREN: Terminal.LPAREN,
            TokenType.DELIMITER_RPAREN: Terminal.RPAREN,
            TokenType.OPERATOR_EQ: Terminal.EQ,
            TokenType.OPERATOR_GT: Terminal.GT,
            TokenType.OPERATOR_LT: Terminal.LT,
            TokenType.OPERATOR_GE: Terminal.GE,
            TokenType.OPERATOR_LE: Terminal.LE,
            TokenType.OPERATOR_NE: Terminal.NE,
            TokenType.OPERATOR_AND: Terminal.AND,
            TokenType.OPERATOR_OR: Terminal.OR,
            TokenType.OPERATOR_NOT: Terminal.NOT,
            TokenType.OPERATOR_PLUS: Terminal.PLUS,
            TokenType.OPERATOR_MINUS: Terminal.MINUS,
            TokenType.CONSTANT_NUMBER: Terminal.NUMBER,
            TokenType.CONSTANT_STRING: Terminal.STRING,
            TokenType.IDENTIFIER: Terminal.IDENTIFIER
        }

        return token_type_to_terminal.get(token.type, None)

    def _record_step(self, action, expression=None):
        """记录分析步骤（四元式）"""
        self.step += 1
        stack_str = str([str(symbol) for symbol in self.stack])
        input_str = str([str(self._token_to_terminal(token)) for token in self.tokens[self.current_token_index:]])

        quad = [self.step, stack_str, input_str, action]
        if expression:
            quad.append(str(expression))

        self.output.append(quad)
        return quad

    def error(self, message, expected=None):
        """抛出语法错误"""
        if self.current_token:
            line = self.current_token.line
            column = self.current_token.column
        else:
            # Handle case where error occurs at the end of token stream
            last_token = self.tokens[-1] if self.tokens else None
            line = last_token.line if last_token else 1
            column = last_token.column if last_token else 1

        raise ParserError(message, line, column, expected)

    def advance(self):
        """前进到下一个Token"""
        self.current_token_index += 1
        if self.current_token_index < len(self.tokens):
            self.current_token = self.tokens[self.current_token_index]
        else:
            self.current_token = Token(TokenType.EOF, '$', -1, -1)  # Use a sentinel EOF token

    def parse(self):
        """开始预测分析"""
        try:
            # 初始化分析栈
            self.stack.append(Terminal.EOF)
            self.stack.append(NonTerminal.PROGRAM)

            self._record_step("初始化分析栈")

            while self.stack:
                top = self.stack[-1]
                # Ensure current_token is never None to avoid errors in _token_to_terminal
                if self.current_token is None:
                    self.current_token = Token(TokenType.EOF, '$', -1, -1)

                current_terminal = self._token_to_terminal(self.current_token)

                self._record_step(f"分析栈顶: {top}, 当前输入: {current_terminal}")

                if isinstance(top, Terminal):
                    if top == Terminal.EPSILON:  # Should not happen if grammar is correct, but for safety
                        self.stack.pop()
                        continue

                    if top == current_terminal:
                        self.stack.pop()
                        self._handle_terminal_match(top)  # Build leaf AST nodes
                        self.advance()
                        self._record_step(f"匹配终结符: {top}")
                    else:
                        expected_terminals = self.analyzer.first_sets.get(self.stack[-2], set()) if len(
                            self.stack) > 1 and isinstance(self.stack[-2], NonTerminal) else {top}
                        self.error(f"Unexpected token '{self.current_token.value}'", expected=expected_terminals)


                elif isinstance(top, NonTerminal):
                    key = (top, current_terminal)
                    if key in self.predictive_table:
                        production = self.predictive_table[key]
                        self.stack.pop()

                        # ==================== 修改部分：压入ActionSymbol ====================
                        # 先压入动作符号，再逆序压入产生式右部
                        # 这样，当产生式右部全部匹配完后，动作符号会到栈顶
                        action = ActionSymbol(top, production)
                        self.stack.append(action)
                        # =================================================================

                        # 将产生式右部逆序压入栈中
                        for symbol in reversed(production):
                            if symbol != Terminal.EPSILON:
                                self.stack.append(symbol)

                        self._record_step(f"应用产生式: {top} -> {' '.join(str(s) for s in production)}")

                    else:
                        # Better error reporting
                        expected_terminals = self.analyzer.first_sets.get(top, set())
                        self.error(f"Unexpected token '{self.current_token.value}' while trying to parse {top}",
                                   expected=expected_terminals)

                # ==================== 新增部分：处理ActionSymbol ====================
                elif isinstance(top, ActionSymbol):
                    self.stack.pop()  # 弹出动作符号
                    self._handle_production(top.non_terminal, top.production)
                    self._record_step(f"执行语义动作: {top}")
                # =================================================================

                else:
                    self.error(f"Unexpected symbol on stack: {top}")

            # After loop, check if we consumed all tokens
            if self.current_token.type != TokenType.EOF:
                self.error(f"Unexpected token '{self.current_token.value}' after end of program")

            # 解析完成后，从AST构建栈中获取最终结果
            if self.ast_build_stack:
                ast_nodes = list(self.ast_build_stack)
                # 清理AST栈，只保留语句节点
                final_ast = [node for node in ast_nodes if isinstance(node, (SelectStatement, CreateTableStatement,
                                                                             InsertStatement, DeleteStatement,UpdateStatement))]
                return final_ast, self.output
            else:
                return [], self.output

        except ParserError as e:
            error_quad = [self.step + 1, str([str(s) for s in self.stack]),
                          str([str(self._token_to_terminal(token)) for token in
                               self.tokens[self.current_token_index:]]),
                          f"ERROR: {str(e)}"]
            self.output.append(error_quad)
            print(f"Parser Error: {e}")
            return None, self.output

    def _handle_terminal_match(self, terminal):
        """当终结符匹配时，创建并压入AST叶子节点"""
        if terminal == Terminal.IDENTIFIER and self.current_token:
            identifier = Identifier(self.current_token.value)
            self.ast_build_stack.append(identifier)

        elif terminal == Terminal.NUMBER and self.current_token:
            val = self.current_token.value
            try:
                num_val = int(val) if '.' not in val else float(val)
                constant = Constant(num_val)
                self.ast_build_stack.append(constant)
            except ValueError:
                self.error(f"Invalid number format: {val}")

        elif terminal == Terminal.STRING and self.current_token:
            constant = Constant(self.current_token.value)
            self.ast_build_stack.append(constant)

        elif terminal in (Terminal.INTEGER, Terminal.VARCHAR, Terminal.FLOAT,
                          Terminal.BOOLEAN, Terminal.Date) and self.current_token:
            dtype = self.current_token.value.upper()
            self.ast_build_stack.append(dtype)

        elif terminal == Terminal.ASTERISK:
            self.ast_build_stack.append("*")

    def _handle_production(self, non_terminal, production):
        """在产生式匹配完成后，构建AST节点。"""
        print(f"Reducing by {non_terminal} -> {' '.join(map(str, production))}. AST Stack: {self.ast_build_stack}")
        # --- SELECT Statement Rules ---
        if non_terminal == NonTerminal.SELECT_STMT:
            # SELECT_STMT → SELECT_CORE OPTIONAL_WHERE_CLAUSE
            where_clause = self.ast_build_stack.pop()
            select_core = self.ast_build_stack.pop()
            if isinstance(select_core, SelectStatement):
                if where_clause is not None:
                    select_core.where_clause = where_clause
                self.ast_build_stack.append(select_core)
            print(f"Built SELECT_STMT. AST Stack: {self.ast_build_stack}")
        elif non_terminal == NonTerminal.SELECT_CORE:
            # SELECT_CORE → SELECT TARGET_LIST FROM TABLE_NAME
            table_name = self.ast_build_stack.pop()
            target_list = self.ast_build_stack.pop()
            if isinstance(target_list, list) and isinstance(table_name, Identifier):
                select_stmt = SelectStatement(target_list, table_name, None)
                self.ast_build_stack.append(select_stmt)

        elif non_terminal == NonTerminal.TARGET_LIST:
            # TARGET_LIST → ASTERISK | IDENTIFIER TARGET_LIST_TAIL
            if production[0] == Terminal.ASTERISK:
                first_target = self.ast_build_stack.pop()  # Pop '*'
                targets = [first_target]
                self.ast_build_stack.append(targets)
            else:  # IDENTIFIER TARGET_LIST_TAIL
                target_list_tail = self.ast_build_stack.pop()
                first_target = self.ast_build_stack.pop()
                targets = [first_target] + target_list_tail
                self.ast_build_stack.append(targets)

        elif non_terminal == NonTerminal.TARGET_LIST_TAIL:
            # TARGET_LIST_TAIL → COMMA IDENTIFIER TARGET_LIST_TAIL | ε
            if production[0] == Terminal.EPSILON:
                self.ast_build_stack.append([])  # Empty tail
            else:
                tail = self.ast_build_stack.pop()
                identifier = self.ast_build_stack.pop()
                targets = [identifier] + tail
                self.ast_build_stack.append(targets)


        elif non_terminal == NonTerminal.OPTIONAL_WHERE_CLAUSE:
            # OPTIONAL_WHERE_CLAUSE -> WHERE_CLAUSE
            # OPTIONAL_WHERE_CLAUSE -> ε
            if not production or production[0] == Terminal.EPSILON:
                # 如果是空产生式，压入 None 占位
                self.ast_build_stack.append(None)
                print(f"Built OPTIONAL_WHERE_CLAUSE (ε). Pushed None. AST Stack: {self.ast_build_stack}")

        # --- CREATE TABLE Statement Rules ---
        elif non_terminal == NonTerminal.CREATE_TABLE_STMT:
            # CREATE_TABLE_STMT → CREATE TABLE TABLE_NAME ( COL_DEF_LIST )
            col_def_list = self.ast_build_stack.pop()
            table_name = self.ast_build_stack.pop()
            if isinstance(table_name, Identifier) and isinstance(col_def_list, list):
                create_stmt = CreateTableStatement(table_name, col_def_list)
                self.ast_build_stack.append(create_stmt)
            else:
                self.error("Internal error building CREATE TABLE statement.")

        elif non_terminal == NonTerminal.COL_DEF_LIST:
            # COL_DEF_LIST → COL_DEF COL_DEF_LIST_TAIL
            tail = self.ast_build_stack.pop()
            col_def = self.ast_build_stack.pop()
            col_list = [col_def] + tail
            self.ast_build_stack.append(col_list)

        elif non_terminal == NonTerminal.COL_DEF_LIST_TAIL:
            # COL_DEF_LIST_TAIL → , COL_DEF COL_DEF_LIST_TAIL | ε
            if production[0] == Terminal.EPSILON:
                self.ast_build_stack.append([])  # Empty tail
            else:
                tail = self.ast_build_stack.pop()
                col_def = self.ast_build_stack.pop()
                col_list = [col_def] + tail
                self.ast_build_stack.append(col_list)

        elif non_terminal == NonTerminal.COL_DEF:
            # COL_DEF → IDENTIFIER DATA_TYPE
            data_type = self.ast_build_stack.pop()
            col_name = self.ast_build_stack.pop()
            if isinstance(col_name, Identifier) and isinstance(data_type, str):
                # Build (column_name_string, data_type_string) tuple
                self.ast_build_stack.append((col_name.name, data_type))
            else:
                self.error("Internal error building column definition.")

        # --- UPDATE Statement Rules ---
        elif non_terminal == NonTerminal.UPDATE_STMT:
            # UPDATE_STMT → UPDATE_CORE OPTIONAL_WHERE_CLAUSE
            where_clause = self.ast_build_stack.pop()
            update_core = self.ast_build_stack.pop()
            if isinstance(update_core, UpdateStatement):
                if where_clause is not None:
                    update_core.where_clause = where_clause
                self.ast_build_stack.append(update_core)
                print(f"Built UPDATE_STMT. AST Stack: {self.ast_build_stack}")

        elif non_terminal == NonTerminal.UPDATE_CORE:
            # UPDATE_CORE → UPDATE TABLE_NAME SET SET_LIST
            set_list = self.ast_build_stack.pop()
            table_name = self.ast_build_stack.pop()
            if isinstance(table_name, Identifier) and isinstance(set_list, list):
                update_stmt = UpdateStatement(table_name, set_list, None)
                self.ast_build_stack.append(update_stmt)
            print(f"Built UPDATE_CORE. AST Stack: {self.ast_build_stack}")

        # elif non_terminal == NonTerminal.SET_LIST:
        #     print("3ast_build_stack:",self.ast_build_stack)
        #     # SET_LIST → ASSIGNMENT SET_LIST_TAIL
        #     tail = self.ast_build_stack.pop()
        #     assignment = self.ast_build_stack.pop()
        #     assignments = [assignment] + tail
        #     self.ast_build_stack.append(assignments)
        #
        # elif non_terminal == NonTerminal.SET_LIST_TAIL:
        #     print("4ast_build_stack:",self.ast_build_stack)
        #     # SET_LIST_TAIL → , ASSIGNMENT SET_LIST_TAIL | ε
        #     if production[0] == Terminal.EPSILON:
        #         self.ast_build_stack.append([])  # Empty tail
        #     else:
        #         tail = self.ast_build_stack.pop()
        #         assignment = self.ast_build_stack.pop()
        #         assignments = [assignment] + tail
        #         self.ast_build_stack.append(assignments)
        #
        # elif non_terminal == NonTerminal.ASSIGNMENT:
        #     print("5ast_build_stack:",self.ast_build_stack)
        #     # ASSIGNMENT → IDENTIFIER = EXPR
        #     expr = self.ast_build_stack.pop()
        #     column_name = self.ast_build_stack.pop()
        #     if isinstance(column_name, Identifier):
        #         assignment = (column_name.name, expr)
        #         self.ast_build_stack.append(assignment)
        #     else:
        #         self.error("Internal error building assignment.")

        # --- INSERT Statement Rules ---
        elif non_terminal == NonTerminal.INSERT_STMT:
            # INSERT_STMT → INSERT INTO TABLE_NAME ( TARGET_LIST ) VALUES ( VAL_LIST )
            val_list = self.ast_build_stack.pop()
            target_list = self.ast_build_stack.pop()
            table_name = self.ast_build_stack.pop()
            if isinstance(table_name, Identifier) and isinstance(target_list, list) and isinstance(val_list, list):
                insert_stmt = InsertStatement(table_name, target_list, val_list)
                self.ast_build_stack.append(insert_stmt)
            else:
                self.error("Internal error building INSERT statement.")

        elif non_terminal == NonTerminal.VAL_LIST:
            # VAL_LIST → VALUE VAL_LIST_TAIL
            tail = self.ast_build_stack.pop()
            value = self.ast_build_stack.pop()
            values = [value] + tail
            self.ast_build_stack.append(values)

        elif non_terminal == NonTerminal.VAL_LIST_TAIL:
            # VAL_LIST_TAIL → , VALUE VAL_LIST_TAIL | ε
            if production[0] == Terminal.EPSILON:
                self.ast_build_stack.append([])  # Empty tail
            else:
                tail = self.ast_build_stack.pop()
                value = self.ast_build_stack.pop()
                values = [value] + tail
                self.ast_build_stack.append(values)

        # --- DELETE Statement Rules ---
        elif non_terminal == NonTerminal.DELETE_STMT:
            # DELETE_STMT → DELETE_CORE OPTIONAL_WHERE_CLAUSE
            where_clause = self.ast_build_stack.pop()
            delete_core = self.ast_build_stack.pop()
            if isinstance(delete_core, DeleteStatement):
                if where_clause is not None:
                    delete_core.where_clause = where_clause
                self.ast_build_stack.append(delete_core)
                print(f"Built DELETE_STMT. AST Stack: {self.ast_build_stack}")

        elif non_terminal == NonTerminal.DELETE_CORE:
            # DELETE_CORE → DELETE FROM TABLE_NAME
            table_name = self.ast_build_stack.pop()
            if isinstance(table_name, Identifier):
                delete_stmt = DeleteStatement(table_name, None)
                self.ast_build_stack.append(delete_stmt)

        elif non_terminal == NonTerminal.WHERE_CLAUSE:
            # WHERE_CLAUSE -> WHERE CONDITION
            condition = self.ast_build_stack.pop()  # 弹出 BinaryExpression
            where_node = WhereClause(condition)
            self.ast_build_stack.append(where_node)
            print(f"Built WHERE_CLAUSE. AST Stack: {self.ast_build_stack}")

        elif non_terminal == NonTerminal.CONDITION:
            # CONDITION -> EXPR COMP_OP EXPR
            right_expr = self.ast_build_stack.pop()
            op = self.ast_build_stack.pop()
            left_expr = self.ast_build_stack.pop()
            condition_node = BinaryExpression(left_expr, op, right_expr)
            self.ast_build_stack.append(condition_node)
            print(f"Built CONDITION. AST Stack: {self.ast_build_stack}")

        elif non_terminal == NonTerminal.COMP_OP:
            # COMP_OP -> EQ | GT | LT | ...
            # 我们需要将操作符本身压入栈中，以便 CONDITION 节点使用
            # 注意：产生式只有一个终结符，例如 [Terminal.EQ]
            op_terminal = production[0]
            self.ast_build_stack.append(op_terminal)
            print(f"Pushed COMP_OP ({op_terminal}). AST Stack: {self.ast_build_stack}")

            ### --- 新增 UPDATE 的 SET 子句处理逻辑 --- ###
        elif non_terminal == NonTerminal.ASSIGNMENT:
            # ASSIGNMENT -> IDENTIFIER EQ EXPR
            expr = self.ast_build_stack.pop()
            identifier = self.ast_build_stack.pop()
            assignment_node = Assignment(identifier, expr)
            self.ast_build_stack.append(assignment_node)
            print(f"Built ASSIGNMENT. AST Stack: {self.ast_build_stack}")

        elif non_terminal == NonTerminal.SET_LIST:
            # SET_LIST -> ASSIGNMENT SET_LIST_TAIL
            tail_list = self.ast_build_stack.pop()  # 来自 SET_LIST_TAIL 的列表
            head_assignment = self.ast_build_stack.pop()
            # 将头部的赋值语句和尾部的列表合并
            self.ast_build_stack.append([head_assignment] + tail_list)
            print(f"Built SET_LIST. AST Stack: {self.ast_build_stack}")

        elif non_terminal == NonTerminal.SET_LIST_TAIL:
            # SET_LIST_TAIL -> , ASSIGNMENT SET_LIST_TAIL
            # SET_LIST_TAIL -> ε
            if len(production) > 1:  # 对应非空产生式
                tail_list = self.ast_build_stack.pop()
                head_assignment = self.ast_build_stack.pop()
                self.ast_build_stack.append([head_assignment] + tail_list)
            else:  # 对应 ε 产生式
                self.ast_build_stack.append([])  # 递归基，压入一个空列表
            print(f"Built SET_LIST_TAIL. AST Stack: {self.ast_build_stack}")